#include <sobjc-commons/defines.h>
#include <sobjc-commons/utils.h>
#include "sobjc/class.h"
#include "sobjc/runtime.h"
#include "sobjc/selector.h"
#include "protocol.h"

static Class Protocol_class = NULL;

Protocol *
objc_make_protocol(const char *name)
{
  ProtocolPrivate p;

  if (Protocol_class == NULL)
    Protocol_class = objc_lookup_class("Protocol");
  p = PROTOCOL(objc_class_make_instance(Protocol_class));
  p->name = objc_strdup(name);
  p->inst_meths = NULL;
  p->opt_inst_meths = NULL;
  p->class_meths = NULL;
  p->opt_class_meths = NULL;

  return (Protocol *)p;
}

const char *
objc_protocol_get_name(Protocol *p)
{
  return PROTOCOL(p)->name;
}

ProtocolList
objc_protocol_get_protocol_list(Protocol *p)
{
  return PROTOCOL(p)->protocols;
}

MethodDescriptionList
objc_protocol_get_method_description_list(Protocol *p,
                                          BOOL required,
                                          BOOL inst)
{
  if (required)
    if (inst)
      return PROTOCOL(p)->inst_meths;
    else
      return PROTOCOL(p)->class_meths;
  else
    if (inst)
      return PROTOCOL(p)->opt_inst_meths;
    else
      return PROTOCOL(p)->opt_class_meths;
}

MethodDescription
objc_protocol_get_method_description(Protocol *p, SEL sel,
                                     BOOL required, BOOL inst)
{
  MethodDescription desc;
  MethodDescriptionList descs;

  descs = objc_protocol_get_method_description_list(p, required, inst);
  while (descs != NULL) {
    desc = descs->car;
    if (desc->name == sel)
      return desc;
    descs = descs->cdr;
  }

  return NULL;
}

void
objc_protocol_add_instance_method(Protocol *p, const char *name, const char *types)
{
  ProtocolPrivate pp;
  MethodDescription desc;

  pp = PROTOCOL(p);
  desc = objc_make_method_description(name, types);
  pp->inst_meths = objc_method_description_list_add(pp->inst_meths, desc);
}

void
objc_protocol_add_optional_instance_method(Protocol *p, const char *name, const char *types)
{
  ProtocolPrivate pp;
  MethodDescription desc;

  pp = PROTOCOL(p);
  desc = objc_make_method_description(name, types);
  pp->opt_inst_meths = objc_method_description_list_add(pp->opt_inst_meths, desc);
}

void
objc_protocol_add_class_method(Protocol *p, const char *name, const char *types)
{
  ProtocolPrivate pp;
  MethodDescription desc;

  pp = PROTOCOL(p);
  desc = objc_make_method_description(name, types);
  pp->class_meths = objc_method_description_list_add(pp->class_meths, desc);
}

void
objc_protocol_add_optional_class_method(Protocol *p, const char *name, const char *types)
{
  ProtocolPrivate pp;
  MethodDescription desc;

  pp = PROTOCOL(p);
  desc = objc_make_method_description(name, types);
  pp->opt_class_meths = objc_method_description_list_add(pp->opt_class_meths, desc);
}

BOOL
objc_protocol_conforms_to(Protocol *p1 __attribute__((unused)),
                          Protocol *p2 __attribute__((unused)))
{
  return NO;
}

MethodDescription
objc_make_method_description(const char *name, const char *types)
{
  MethodDescription desc;

  desc = ALLOC(struct objc_method_description);
  desc->name = objc_register_sel(name);
  desc->types = objc_strdup(types);
  return desc;
}

SEL
objc_method_description_get_name(MethodDescription desc)
{
  return desc->name;
}

BOOL
objc_method_description_is_required(MethodDescription desc)
{
  return desc->required;
}

BOOL
objc_method_description_is_inst_method(MethodDescription desc)
{
  return desc->inst;
}

